home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Languguage OS 2
/
Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO
/
gnu
/
libg_261.zip
/
libg_261
/
libg++
/
etc
/
lf
/
sort.cc
< prev
Wrap
C/C++ Source or Header
|
1994-05-13
|
6KB
|
182 lines
#include <stdio.h>
#include <ctype.h>
#include <std.h>
/* the next 6 #defines implement a very fast in-line stack abstraction */
#define MAX_STACK_SIZE 32
#define DISPOSE_STACK(S) (free(S))
#define PUSH(LOW,HIGH) do {top->lo = LOW;top++->hi = HIGH;} while (0)
#define POP(LOW,HIGH) do {LOW = (--top)->lo;HIGH = top->hi;} while (0)
#define STACK_NOT_EMPTY (stack < top)
#define SWAP(A,B) do {char *_temp = (A);(A) = (B);(B) = _temp;} while (0)
/* Discontinue quicksort algorithm when partition gets below this size.
This particular magic number was chosen to work best on a Sun 4/260. */
#define MAX_THRESH 30
/* Data structure for stack of unfulfilled obligations. */
typedef struct
{
char **lo;
char **hi;
} stack_node;
/* Once main array is partially sorted by quicksort the remainder is
completely sorted using insertion sort, since this is efficient
for partitions below MAX_THRESH size. BASE points to the beginning
of the array to sort, and END_PTR points at the very last element in
the array (*not* one beyond it!). */
inline static void
insert_sort (char **base_ptr, char **end_ptr)
{
char **run_ptr;
char **tmp_ptr = base_ptr;
#if defined (__GNUC__) && ! defined (__STRICT_ANSI__)
char **thresh = end_ptr <? base_ptr + MAX_THRESH;
#else
char **thresh = base_ptr + MAX_THRESH;
if (thresh > end_ptr)
thresh = end_ptr;
#endif
/* Find the smallest element in the first threshold and put it at the
end of the array. This is guaranteed to be the smallest element in
the array, and it speeds up the inner loop of insertion sort. */
for (run_ptr = tmp_ptr + 1; run_ptr <= thresh; run_ptr++)
if (strcmp (*run_ptr, *tmp_ptr) < 0)
tmp_ptr = run_ptr;
SWAP (*tmp_ptr, *base_ptr);
/* Typical insertion sort, but we run from the `right-hand-side'
downto the `left-hand-side.' */
for (run_ptr = base_ptr + 1; run_ptr < end_ptr; run_ptr++)
{
char *temp = *(tmp_ptr = run_ptr + 1);
/* Select the correct location for the new element,
by sliding everyone down by 1 to make room! */
while (strcmp (temp, *--tmp_ptr) < 0)
tmp_ptr[1] = *tmp_ptr;
tmp_ptr[1] = temp;
}
}
/* Return the median value selected from among the
LOW, MIDDLE, and HIGH. Rearrange LOW and HIGH so
the three values are sorted amongst themselves.
This speeds up later work... */
inline static char *
find_pivot (char **low, char **high)
{
char **middle = low + ((high - low ) >> 1);
if (strcmp (*middle, *low) < 0)
SWAP (*middle, *low);
if (strcmp (*high, *middle) < 0)
SWAP (*middle, *high);
else
return *middle;
if (strcmp (*middle, *low) < 0)
SWAP (*middle, *low);
return *middle;
}
/* Order elements using quicksort. This implementation incorporates
4 optimizations discussed in Sedgewick:
1. Non-recursive, using an explicit stack of log (n) pointers that
indicate the next array partition to sort.
2. Choses the pivot element to be the median-of-three, reducing
the probability of selecting a bad pivot value.
3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
insertion sort to sort within the partitions. This is a
big win, since insertion sort is faster for small, mostly
sorted array segements.
4. The larger of the 2 sub-partitions are always pushed onto the
stack first, with the algorithm then concentrating on the
smaller partition. This *guarantees* no more than log (n)
stack size is needed! */
void
sort (char **base_ptr, int total_elems)
{
if (total_elems > MAX_THRESH)
{
char **lo = base_ptr;
char **hi = lo + (total_elems - 1);
char **left_ptr;
char **right_ptr;
stack_node stack[MAX_STACK_SIZE];
stack_node *top = stack + 1;
while (STACK_NOT_EMPTY)
{
/* Select the median-of-three here. This allows us to
skip forward for the LEFT_PTR and RIGHT_PTR. */
char *pivot = find_pivot (lo, hi);
left_ptr = lo + 1;
right_ptr = hi - 1;
/* Here's the famous ``collapse the walls'' section of
quicksort. Gotta like those tight inner loops! */
do
{
while (strcmp (*left_ptr, pivot) < 0)
left_ptr++;
while (strcmp (pivot, *right_ptr) < 0)
right_ptr--;
if (left_ptr < right_ptr)
{
SWAP (*left_ptr, *right_ptr);
left_ptr++;
right_ptr--;
}
else if (left_ptr == right_ptr)
{
left_ptr++;
right_ptr--;
break;
}
}
while (left_ptr <= right_ptr);
if ((right_ptr - lo) <= MAX_THRESH)
{
if ((hi - left_ptr) <= MAX_THRESH) /* ignore both small tables */
POP (lo, hi);
else /* ignore small left table */
lo = left_ptr;
}
else if ((hi - left_ptr) <= MAX_THRESH) /* ignore small right table */
hi = right_ptr;
else if ((right_ptr - lo) > (hi - left_ptr))
{ /* push larger left table */
PUSH (lo, right_ptr);
lo = left_ptr;
}
else
{ /* push larger right table */
PUSH (left_ptr, hi);
hi = right_ptr;
}
}
}
insert_sort (base_ptr, base_ptr + (total_elems - 1));
}